//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Resource Application Block
//===============================================================================

using System;
using System.Diagnostics.CodeAnalysis;
using System.Collections;
using System.Reflection;
using System.Windows.Forms;
using EntLibContrib.Resource.Configuration;

namespace EntLibContrib.Resource.Configuration.Design.Editors
{
  /// <summary>
  /// Handles the building of the tree view based on assemblies and embedded resources.
  /// </summary>
  internal class ResourceSelector
  {
		#region Fields
		private TreeNode rootNode;
    private AssemblyResourceName currentAssemblyResourceName;
    private TreeNode currentResourceTreeNode;
    private TreeView treeView;
    private Hashtable loadedAssemblies;
		#endregion

		#region Construction
		/// <summary>
		/// Initializes a new instance of the <see cref="ResourceSelector"/> class.
		/// </summary>
		/// <param name="currentAssemblyResourceName">Name of the current assembly resource.</param>
		/// <param name="treeView">The tree view.</param>
    public ResourceSelector(AssemblyResourceName currentAssemblyResourceName, TreeView treeView)
    {
      if (treeView == null)
      {
        throw new ArgumentNullException("treeView");
      }
      loadedAssemblies = new Hashtable();
      this.currentAssemblyResourceName = currentAssemblyResourceName;
      this.treeView = treeView;

      LoadResources();
    }
		#endregion

		#region Public Methods
		/// <summary>
		/// Loads the tree view.
		/// </summary>
		/// <param name="assembly">The assembly.</param>
		/// <returns>True if the node was successfully loaded</returns>
    public bool LoadTreeView(Assembly assembly)
    {
      if (loadedAssemblies.Contains(assembly.FullName))
      {
        return true;
      }

      TreeNodeTable nodeTable = new TreeNodeTable(assembly);
      string[] resources = LoadResourcesFromAssembly(assembly);
      if (resources.Length == 0)
      {
        return false;
      }

      LoadResourceNodes(assembly, resources, nodeTable);

      return AddResourcesToTreeView(assembly, nodeTable);
    }
    #endregion

		#region Private Methods
		/// <summary>
		/// Loads the resources.
		/// </summary>
		private void LoadResources()
		{
			this.rootNode = new TreeNode(InternalResources.AssembliesLabelText, 0, 1);
			this.treeView.Nodes.Add(rootNode);
			foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
			{
				this.LoadTreeView(assembly);
			}
			rootNode.ExpandAll();
			this.treeView.SelectedNode = this.currentResourceTreeNode;
		}

		/// <summary>
		/// Loads the resources from an assembly.
		/// </summary>
		/// <param name="assembly">The assembly.</param>
		/// <returns>List of resources names</returns>
		[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
		private static string[] LoadResourcesFromAssembly(Assembly assembly)
    {
			string[] embeddedResources = null;
			ArrayList localizedResources = new ArrayList();

			try
			{
				embeddedResources = assembly.GetManifestResourceNames();
			}
			catch 
			{
				// fix for dynamically loaded assemblies that throws a NotSupportedException
			}

			if (embeddedResources == null)
				return new string[] { };
			else
			{
				foreach (string resource in embeddedResources)
				{
					if (resource.EndsWith(ResourceManagerSettings.AssemblyExtension))
					{
						localizedResources.Add(resource.Substring(0, resource.Length - 10));
					}
				}
				if (localizedResources.Count == 0)
				{
					return new string[] { };
				}
				else
				{
					string[] resources = new string[localizedResources.Count];
					localizedResources.CopyTo(resources);
					return resources;
				}
			}
		}

		/// <summary>
		/// Loads the resource nodes.
		/// </summary>
		/// <param name="assembly">The assembly.</param>
		/// <param name="resources">The resources.</param>
		/// <param name="nodeTable">The node table.</param>
    private void LoadResourceNodes(Assembly assembly, string[] resources, TreeNodeTable nodeTable)
    {
      if (resources == null)
      {
        return;
      }

      foreach (string resource in resources)
      {
        TreeNode newNode = nodeTable.AddResource(resource);
        if (assembly.FullName == this.currentAssemblyResourceName.AssemblyName && resource == this.currentAssemblyResourceName.BaseName)
        {
          this.currentResourceTreeNode = newNode;
        }
      }
    }

		/// <summary>
		/// Adds the resources to the tree view.
		/// </summary>
		/// <param name="assembly">The assembly.</param>
		/// <param name="nodeTable">The node table.</param>
		/// <returns>true on successfully adding an assembly node</returns>
		private bool AddResourcesToTreeView(Assembly assembly, TreeNodeTable nodeTable)
		{
			if (nodeTable.AssemblyNode.Nodes.Count > 0)
			{
				this.rootNode.Nodes.Add(nodeTable.AssemblyNode);
				this.rootNode.ExpandAll();
				loadedAssemblies[assembly.FullName] = 1;
				return true;
			}
			else
			{
				return false;
			}
		}
		#endregion

		#region Sub Classes
    /// <summary>
    /// Represents the table of tree nodes by assembly resource.
    /// </summary>
    private class TreeNodeTable
    {
			#region Fields
      private TreeNode assemblyNode;
      private string assemblyName;
			#endregion

			#region Properties
			/// <summary>
			/// Gets the assembly node.
			/// </summary>
			/// <value>The assembly node.</value>
			public TreeNode AssemblyNode
			{
				get
				{
					return this.assemblyNode;
				}
			}
			#endregion

			#region Construction
			/// <summary>
			/// Initializes a new instance of the <see cref="TreeNodeTable"/> class.
			/// </summary>
			/// <param name="assembly">The assembly.</param>
			public TreeNodeTable(Assembly assembly)
      {
        this.assemblyNode = new TreeNode(assembly.GetName().Name, (int)ResourceImages.Assembly, (int)ResourceImages.Assembly);
        this.assemblyName = assembly.FullName;
      }
			#endregion

			#region Public Methods
			/// <summary>
			/// Adds the resource to a tree node.
			/// </summary>
			/// <param name="resource">The resource.</param>
			/// <returns>the resource node just added</returns>
			public TreeNode AddResource(string resource)
      {
				AssemblyResourceName assemblyResourceName = new AssemblyResourceName(this.assemblyName, resource);
        TreeNode resourceNode = new TreeNode(resource, (int)ResourceImages.Resource, (int)ResourceImages.Resource);
        resourceNode.Tag = assemblyResourceName;
        this.assemblyNode.Nodes.Add(resourceNode);
        return resourceNode;
      }
			#endregion
		}
		#endregion
	}
}